﻿#pragma once

#include "../../thirdparty/klogger/klogger/interface/logger.h"
#include "../lang/lang.hh"
#include "../util/box_std_allocator.hh"
#include "../util/spsc_queue.hpp"
#include "../util/spsc_queue_pair.hpp"
#include "box_network_event.hh"
#include <atomic>
#include <memory>
#include <string>
#include <thread>
#include <unordered_map>
#include "../util/box_std_allocator.hh"

namespace kratos {
namespace config {
class BoxConfig;
}
} // namespace kratos

namespace klogger {
class Appender;
} // namespace klogger

// 前置声明，依赖knet
typedef struct _loop_t kloop_t;
typedef struct _channel_ref_t kchannel_ref_t;

namespace kratos {
namespace service {

class BoxChannel;
class ServiceBox;

/**
 * 网络
 */
class BoxNetwork {
  using MainChannelMap =
    PoolUnorederedMap<
      std::uint64_t,
      std::shared_ptr<BoxChannel>
    >;

  using ChannelMap =
    PoolUnorederedMap<
      std::uint64_t,
      kchannel_ref_t*
    >;

  using ListenerNameMap =
    PoolUnorederedMap <
      std::uint64_t,
      std::pair<std::uint64_t, std::string>
    >;

  using SPSCQueue     = kratos::corelib::SPSCQueue<NetEventData>;
  using SPSCQueuePair = kratos::corelib::SPSCQueuePair<NetEventData>;

  SPSCQueue main_to_net_queue; ///< 单读单写队列-生产者：主线程，消费者：网络线程
  SPSCQueue net_to_main_queue; ///< 单读单写队列-生产者：网络线程，消费者：主线程

  SPSCQueuePair main_queue; ///< 队列对，方便读写
  SPSCQueuePair net_queue;  ///< 队列对，方便读写

  std::thread      thread_;             ///< 网络线程
  std::atomic_bool running_{false};     ///< 网络线程运行标志
  std::atomic_bool thread_exit_{false}; ///< 网络线程退出标志
  MainChannelMap   main_channel_map_;   ///< 主线程管道查找表
  ChannelMap       thread_channel_map_; ///< 网络线程管道查找表
  ListenerNameMap  listener_name_map_;  ///< 监听器名字查找表
  kloop_t*         loop_{nullptr};      ///< 网络循环

  int box_channel_recv_buffer_len_{1024 * 16}; ///< 当前管道读缓冲区长度, 默认为1024*16

public:
  /**
   * 构造
   */
  BoxNetwork();
  /**
   * 析构
   */
  virtual ~BoxNetwork();
  /**
   * 启动网络，内部会启动一个独立的网络线程.
   * @retval true 成功
   * @retval false 失败
   */
  auto start() -> bool;
  /**
   * 关闭，关闭并等待网络线程关闭.
   * @retval true 成功
   * @retval false 失败
   */
  auto stop() -> bool;
  /**
   * 启动一个网络监听器, 监听host:port.
   * @param name 名称
   * @param host 网络地址或域名
   * @param port 端口
   * @retval true 成功
   * @retval false 失败
   */
  auto listen_at(
    const std::string name,
    const std::string &host,
    int port) -> bool;
  /**
   * 启动一个连接器.
   * @param name 名称
   * @param host 网络地址或域名
   * @param port 端口
   * @retval true 成功
   * @retval false 失败
   */
  auto connect_to(
    const std::string name,
    const std::string &host,
    int port,
    int timeout) -> bool;
  /**
   * 关闭管道.
   * @param id 管道ID
   * @retval true 成功
   * @retval false 失败，失败不论什么原因都会正确释放资源
   */
  auto close_channel(std::uint64_t id) -> bool;
  /**
   * 网络主循环，在逻辑主循环内调用.
   */
  auto update() -> void;
  /**
   * 获取管道实例
   *
   * \param id 管道ID
   * \return 管道实例
   */
  auto get_channel(std::uint64_t id) const noexcept
    -> const std::shared_ptr<BoxChannel> &;
  /**
   * 获取网络线程队列
   *
   * \return 网络线程队列
   */
  auto get_net_queue() noexcept -> SPSCQueuePair &;
  /**
   * 获取逻辑线程队列
   *
   * \return 逻辑线程队列
   */
  auto get_main_queue() noexcept -> SPSCQueuePair &;
  /**
   * 获取工作线程的管道表容器
   *
   * \return 管道表容器
   */
  auto get_worker_channel_map() noexcept -> ChannelMap &;
  /**
   * 添加工作线程管道
   *
   * \param channel_id 管道ID
   * \param channel_ref 管道引用
   */
  auto add_worker_channel(std::uint64_t channel_id,
    kchannel_ref_t* channel_ref) -> bool;
  /**
   * 获取监听器名字表
   */
  auto get_listener_name_map() noexcept -> ListenerNameMap &;
  /**
   * 获取管道接收缓冲区长度
   *
   * \return 接收缓冲区长度
   */
  auto get_channel_recv_buffer_len() const noexcept -> int;

public:
  /**
   * 获取服务容器配置
   *
   * \return 服务容器配置
   */
  virtual auto get_config() -> kratos::config::BoxConfig & = 0;
  /**
   * 获取日志添加器.
   *
   * \return
   */
  virtual auto get_logger_appender() -> klogger::Appender * = 0;

  /**
   * 获取本地化实例.
   *
   * \return 本地化实例
   */
  virtual auto get_lang() -> lang::Lang * = 0;
  /**
   * 开启监听事件
   *
   * \param name 监听器名称
   * \param success 成功或失败
   * \param channel 监听管道
   */
  virtual void on_listen(
    const std::string &name,
    bool success,
    std::shared_ptr<BoxChannel> &channel) = 0;
  /**
   * 接受一个新的连接建立
   *
   * \param channel 新建立的管道
   */
  virtual void on_accept(std::shared_ptr<BoxChannel> &channel) = 0;
  /**
   * 连接到一个远程监听器
   *
   * \param name 连接器名称
   * \param success 成功或失败
   * \param channel 新建立的管道
   */
  virtual void on_connect(
    const std::string &name,
    bool success,
    std::shared_ptr<BoxChannel> &channel) = 0;
  /**
   * 关闭管道事件
   *
   * \param channel 关闭的管道
   */
  virtual void on_close(std::shared_ptr<BoxChannel> &channel) = 0;
  /**
   * 数据事件
   *
   * \param channel 接收到数据的管道
   */
  virtual void on_data(std::shared_ptr<BoxChannel> &channel) = 0;

private:
  /**
   * 网络线程事件处理函数.
   */
  auto network_thread_processor() -> void;
  /**
   * 向网络线程发送数据
   *
   * \param id 管道ID
   * \param data 数据指针
   * \param size 数据长度
   * \return 实际发送的数据长度
   */
  auto enqueue_send_request(std::uint64_t id, const char *data, int size)
      -> int;
  /**
   * 通知网络线程关闭连接
   *
   * \param id 管道ID
   * \return 成功或失败
   */
  auto enqueue_close_request(std::uint64_t id) -> bool;
  /**
   * 启动网络线程
   */
  auto start_worker() -> void;
  /**
   * 网络线程清理 
   */
  auto worker_cleanup() -> void;
  /**
   * 在主线程内处理网络事件
   */
  auto do_event_main(NetEventData &event_data) -> void;
  /**
   * 在网络线程内处理网络事件
   */
  auto do_event_worker(NetEventData &event_data) -> void;
  /**
   * 配置变化重载.
   *
   * \return
   */
  auto on_config_change() -> void;

  /**
   * 写入日志.
   *
   * \param id 语言ID
   * \param level 日志等级
   * \param args 日志参数
   * \return
   */
  template <typename... ARGS>
  auto write_log(lang::LangID id, int level, ARGS... args) {
    if (!get_lang() || !get_logger_appender()) {
      return;
    }
    const auto &fmt = get_lang()->get_lang(id);
    if (fmt.empty()) {
      return;
    }
    get_logger_appender()->write(level, fmt.c_str(), args...);
  }

  friend class BoxChannel;
};

} // namespace service
} // namespace kratos
